iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 25
1

在 Firebase 控制台中編寫資料的讀取和寫入規則,是防止惡意人士的最後一道也是最堅強的防線。如果不編寫資料的使用規則,當初我們在創建 Firebase 應用的時候,不是有一串 config 要拿來給 firebase 做初始化嗎 ? 雖然現在前端有許多打包工具,讓你的程式碼看起來亂亂的,難我們難保有心人士還是有辦法取得我們的設定資料,或者是你的應用程式沒有經過打包,那麼就更有編寫規則的必要了。

一開始我們是以測試模式啟動,會允許所有人來讀取和寫入,現在我們要進到 Firebase 控制台,把原本的規則刪掉,換成我們自己的規則。

規則編寫頁面在這,這是文章撰寫的當下 Firebase 的政策,若以測試模式 ( 只要有當初的 firebase config,任何人都可以對你進行資料操作 ) 運行太久,將會限制資料開放到該時間點,該時間點後會關閉讀寫權限。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819Of6nSDGFHq.jpg

編寫規則的文件,以下的貼圖部分通通裡面有找:

https://firebase.google.com/docs/reference/security/storage

我們都是針對路徑的資料去做保護和編寫規則,所以一開始要先了解 match 路徑:

match /collection名稱/document名稱,只要有 match 到該路徑,就會對該路徑套用規則。

// 1. 使用規則版本2,不用理他,1長什麼樣我也不知道
rules_version = '2';

// 代表 cloud.firestore 服務的規則內容
service cloud.firestore {

	// 這個資料庫下的路徑
  match /databases/{database}/documents {

	// 上面兩層不用動,規則都從現在這裡開始寫
    // 第一條規則開始
    match /collection名稱/document名稱 {
      allow read: if true
      allow write: if request.auth != null && resource.data.authorInfo.uid == request.auth.uid
    }
	
	//match ... 最終你會有不只一條 match

  }
}

這是 match 的基本構型

https://ithelp.ithome.com.tw/upload/images/20201003/201298198IGki3j7OX.jpg

allow 後面你可以接允許哪些行為是這些

https://ithelp.ithome.com.tw/upload/images/20201003/20129819YurAzSUceH.jpg

match 的程式碼區塊可以包含一條以上的 allow 語句,可以加分號也可以不加

 match /collection名稱/{document名稱} {
  allow read
  allow write: if request.auth != null && resource.data.authorInfo.uid == request.auth.uid
}

其中比較有疑問的是 { ... },這個就要看到匹配路徑的解說

https://ithelp.ithome.com.tw/upload/images/20201003/20129819HC9gqwpz27.jpg

這樣就知道我們用 {} 括號起來的,代表集合下的所有文檔。

現在我先清空我們的資料庫文章和管理員,auth 的使用者也通通先刪除全部重來,並且去重新註冊兩個新會員並個別發表一篇文章,在過程中你會在 F12 中看到一些 Firebase 跳出的權限不足的錯誤,這部份我們會一步步來檢查並編寫規則。

可是就在我新增文章時出錯了,居然console...
https://ithelp.ithome.com.tw/upload/images/20201003/201298191rRHA4P1db.jpg

去把錯誤訊息印出來

https://ithelp.ithome.com.tw/upload/images/20201003/20129819ixm2PMwhe7.jpg

告訴我這個,應該是找不到文件路徑或者權限不足,因為過去我們是測試模式,所以不會遇到這種狀況,現在我們要上傳文章,可想而知,我們要先來寫上傳文章的權限。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819jpW7xtkmoa.jpg

那我們文章資料的部份分為幾個狀況:

  1. 上傳新文章 → 誰都可以上傳新文章
  2. 編輯現有文章 → 只有文章擁有者可以編輯自己的現有文章
  3. 閱讀文章 → 誰都可以閱讀文章,如果你想搞什麼付費文章,那就是再開一個 postsForPaid 之類的,不建議再多新增屬性進去單個文章,看你啦我覺得這樣分開一點比較好。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

		// 我們的第一筆 match
    match /posts/{post} {
      allow read;
      allow create: if request.auth != null;
	    allow update: if request.auth != null && resource.data.authorInfo.uid == request.auth.uid;
    }
  }
}

加入我們的第一筆 match 後,會提醒你是否要發佈規則,我們就按發佈等他生效,他說最多不超過一分鐘,更新之後,就可以再去重新上傳一次,並且看到成功了。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819oVNXRC8tJs.jpg

回到資料庫看看,咦 ? 文章是有了沒錯,但是使用者呢 ? 原來是我剛剛註冊完 manager 後,因為太急著去寫一篇文章的關係,忘記了 manager 路徑也要設定規則 ...,所以只能再重新來過了。

給 managers 做匹配後重新註冊 = =

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read;
      allow create: if request.auth != null;
	    allow update: if request.auth != null && resource.data.authorInfo.uid == request.auth.uid;
		}
  	
    match /managers/{manager} {
    	allow read: if request.auth.uid == resource.data.uid;
      allow create: if request.auth != null;
      allow update: if request.auth.uid == resource.data.uid;
    }
  }
}

剛剛註冊的 manager 就出來了

https://ithelp.ithome.com.tw/upload/images/20201003/20129819jICkTL2788.jpg

我再次重來,用兩個 manager,準備來分別弄幾篇文章出來測試能不能修改別人的。
我現在是以使用者B的身分登入,現在我看看能不能修改使用者A的文章

https://ithelp.ithome.com.tw/upload/images/20201003/20129819el3rrJzUSw.jpg

這下子可以看見程式走進更新流程,並且顯示更新失敗,訊息顯示遺失或權限不足。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819ZgW9K4RGXI.jpg

我們再來修改使用者B自己的文章,更新成功

https://ithelp.ithome.com.tw/upload/images/20201003/201298194sJ6Y2Z4yu.jpg

點進去文章看也沒問題。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819fU8fL0Wtak.jpg

接下來,點進文章看的時候也出現了一樣的錯誤訊息,這部分是文章留言,所以現在我們再繼續幫留言的部分添加規則,基本上是誰都可以看,這邊就變成嵌套的 match ,路徑是直接接續父層,是直接串接下來的,按發佈後等一下,重新整理一遍,錯誤沒了,圖就不貼了。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read;
      allow create: if request.auth != null;
	    allow update: if request.auth != null && resource.data.authorInfo.uid == request.auth.uid;
      match /comments/{comment} {
    		allow read;
      }
    }
    
    match /managers/{manager} {
    	allow read: if request.auth.uid == resource.data.uid;
      allow create: if request.auth != null;
      allow update: if request.auth.uid == resource.data.uid;
    }
  }
}

在編寫規則時,我們可以模擬送出需求,測試可不可以通過,基本上我也是都會先測試通過才按發佈,降低一些發佈了結果不能用的機率...。

比如說我先測試修改文章的部分,你就可以像這樣來測試一筆文章在不同的使用者 uid 下,可否通過編輯,方法選 update:

https://ithelp.ithome.com.tw/upload/images/20201003/20129819D8pL5yJYoh.jpg

位置填文章路徑

https://ithelp.ithome.com.tw/upload/images/20201003/20129819R72VstHCce.jpg

拉霸往下填你預期會成功的 uid (2NDZ5xrmFYVzP2B6xscaKHrDlwr1) 並按下執行,看見有匹配成功驗證通過,測試台右方也會顯示 resource 內有什麼,接著你可以 uid 故意多給他打一個字,看看不成功長什麼樣子。

https://ithelp.ithome.com.tw/upload/images/20201003/20129819B5xcV7ZA9L.jpg

這是一些文件定義,建議翻翻:

https://firebase.google.com/docs/reference/rules/rules.firestore.Resource

最後,我們的留言功能還沒有編寫規則,目前留言沒有什麼限制,直接 allow write 就 OK 了,請各位自己試試看吧。

補充一下,登出的部分會報錯,原因應該是登出後跳轉速度太快導致上傳離線狀態失敗,所以幫我把Model 內的登出方法改成先上傳狀態在登出:

async F_signOut () {
  console.log('觸發了F_signOut')
  const self = this
  await self.F_updateManagerInfo(self.$route.params.who, { online: false })
  firebase.auth().signOut().then(function () {
    self.$router.push('/')
    console.log('登出成功')
  })
}

好了,到這邊你的文件有權限防護作用了,一般來說如果有後端的話,可能都會有 API 讓你呼叫來檢查權限,但是在沒有後端的情況下,最慘的狀況就是我們可能要針對使用者的權限等級另外上傳一筆資訊,並且在每次行動前先拉這筆資訊下來進行檢查,會變得非常麻煩,好在 Firebase 有提供權限控管,可以輕易地幫我們處理這個問題。

今天就這樣,下篇不知道XD。


沒事也可以逛逛我們其他團隊成員的文章啦 ~~
eien_zheng: 前端小嘍嘍的Golang學習旅程_The journey of learning Golang 系列
PollyPO技術: 前端設計轉前端工程師-JS踩坑雜記 30 天 系列
阿電: 忍住不打牌位,只要30天VueJS帶你上A牌 系列
喬依司: 實作經典 JavaScript 30 系列


上一篇
Day 24: 為文章準備個留言功能吧
下一篇
Day 26: Firebase Storage + 處理同源政策問題
系列文
Vue CLI + Firebase 雲端資料庫 30天打造簡易部落格及後臺管理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言